package contegor.scire.structure;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Future;

import contegor.scire.core.ConnectableImpl;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;
import contegor.scire.core.Transaction;
import contegor.scire.core.Version;
import contegor.scire.structure.Context.InternalTask;

/**
 * Intraprocess {@link Connection} to a {@link ConceptCloud}.
 *
 * @version $Revision: 1.1 $
 * @author  Héctor Cirbián Pousa ($Author: hector $)
 */
@Version("$Id: IntraprocessConnection.java,v 1.1 2010-02-01 10:56:44 hector Exp $")
public class IntraprocessConnection implements Connection {

  private static class ClonningReturnPoint<T> extends FutureReturnPoint<T> {

    public ClonningReturnPoint() {
      super();
    }

    @SuppressWarnings("unchecked")
    public <U extends T> void manage(TaskId taskId, U result) {
      try {
        if (result instanceof ConnectableImpl)
          result = (U)((ConnectableImpl)result).clone();
        super.manage(taskId, result);
      }
      catch (CloneNotSupportedException e) {
        throw new IllegalArgumentException(e);
      }
    }

    @SuppressWarnings("unchecked")
    public <U extends T> void manage(TaskId taskId, Collection<U> result) {
      try {
        if (result.isEmpty())
          return;
        ArrayList<T> dup  = new ArrayList<T>(result.size());
        Iterator<U>  iter = result.iterator();
        T t = iter.next();
        if (t instanceof ConnectableImpl) {
          dup.add((T)((ConnectableImpl)t).clone());
          while (iter.hasNext())
            dup.add((T)((ConnectableImpl)iter.next()).clone());
          super.manage(taskId, dup);
        }
        else // result contains Id, TaskId or something alike, that does not need to be cloned.
          super.manage(taskId, result);
      }
      catch (CloneNotSupportedException e) {
        throw new IllegalArgumentException(e);
      }    
    }
  }

  private DockingModule dock;
  private Transaction   transaction;
  private boolean       autocommit;
  
  public IntraprocessConnection(DockingModule dock) {
    this.dock = dock;
    this.transaction = null;
    this.autocommit = true;
  }

  public void setAutocommit(boolean autocommit) {
    this.autocommit = autocommit;
  }

  public boolean isAutocommit() {
    return autocommit;
  }

  private <T> void prepareTransaction(Task<T> msg) {
    if (this.autocommit)
      this.transaction = new Transaction(this.autocommit);
    else if (this.transaction == null) {
      this.transaction = Transaction.getCurrent();
      if (this.transaction == null)
        this.transaction = new Transaction(false);
    }
    msg.setTaskId(transaction.nextTaskId());
  }

  @Override
  public <T> Future<List<T>> submit(Task<T> msg) {
    if (msg instanceof InternalTask)
      throw new IllegalArgumentException("Trying to send a Context InternalTask to the cloud.");
    prepareTransaction(msg);
    ClonningReturnPoint<T> crp = new ClonningReturnPoint<T>();
    this.dock.submit(msg, crp);
    return crp;
  }
  
  @Override
  public void commit() throws Exception {
    this.dock.commit(this.transaction.nextTaskId());
    this.transaction = null;
  }

  @Override
  public void rollback() throws Exception {
    this.dock.rollback(this.transaction.nextTaskId());
    this.transaction = null;
  }
}